package tomasulogui;

import tomasulogui.IssuedInst.INST_TYPE;

public class IssueUnit {
  private enum EXEC_TYPE {
    NONE, LOAD, ALU, MULT, DIV, BRANCH} ;

    PipelineSimulator simulator;
    IssuedInst issuee;
    Object fu;
    EXEC_TYPE exType;
    boolean wait;
    int stallCounter = 0;

    public IssueUnit(PipelineSimulator sim) {
      simulator = sim;
    }

    public void execCycle() {
        // an execution cycle involves:
        // 1. checking if ROB and Reservation Station avail
        // 2. issuing to reservation station, if no structural hazard

        // to issue, we make an IssuedInst, filling in what we know
        
        // We check the BTB, and put prediction if branch, updating PC
        //     if pred taken, incr PC otherwise
        
        // We then send this to the ROB, which fills in the data fields
        
        // We then check the CDB, and see if it is broadcasting data we need,
        //    so that we can forward during issue

        // We then send this to the FU, who stores in reservation station
        wait = false;
        
        // ensure the ROB is not full
        if(!simulator.getROB().isFull()){
        
            // create instruction
            Instruction inst = simulator.getMemory().getInstAtAddr(simulator.getPC());
            issuee = IssuedInst.createIssuedInst(inst);
            issuee.setPC(simulator.getPC());

            // Determine instruction Type
            if(issuee.opcode == INST_TYPE.ADD ||
                    issuee.opcode == INST_TYPE.ADDI ||
                    issuee.opcode == INST_TYPE.AND ||
                    issuee.opcode == INST_TYPE.ANDI ||
                    issuee.opcode == INST_TYPE.OR ||
                    issuee.opcode == INST_TYPE.ORI ||
                    issuee.opcode == INST_TYPE.SLL ||
                    issuee.opcode == INST_TYPE.SRA ||
                    issuee.opcode == INST_TYPE.SRL ||
                    issuee.opcode == INST_TYPE.SUB ||
                    issuee.opcode == INST_TYPE.XOR ||
                    issuee.opcode == INST_TYPE.XORI){
                exType = EXEC_TYPE.ALU;

            }
            else if(issuee.determineIfBranch()){
                exType = EXEC_TYPE.BRANCH;
            }
            else if(issuee.opcode == INST_TYPE.DIV){
                exType = EXEC_TYPE.DIV;
            }
            else if(issuee.opcode == INST_TYPE.MUL){
                exType = EXEC_TYPE.MULT;
            }
            else if(issuee.opcode == INST_TYPE.LOAD){
                exType = EXEC_TYPE.LOAD;
            }
            else{
                exType = EXEC_TYPE.NONE;
            }
        
            // ensure a reservation station is available
            if(exType == EXEC_TYPE.ALU && (simulator.getALU().stations[0] == null || simulator.getALU().stations[1] == null)){
                simulator.getROB().updateInstForIssue(issuee);
                //simulator.getROB().readCDB(simulator.getCDB());
                simulator.getALU().acceptIssue(issuee);
            }
            else if(exType == EXEC_TYPE.BRANCH && (simulator.getBranchUnit().stations[0] == null || simulator.getBranchUnit().stations[1] == null)){
                simulator.getROB().updateInstForIssue(issuee);
                
                simulator.getBranchUnit().acceptIssue(issuee);
            }
            else if(exType == EXEC_TYPE.DIV && (simulator.getDivider().stations[0] == null || simulator.getDivider().stations[1] == null)){
                simulator.getROB().updateInstForIssue(issuee);
                //simulator.getROB().readCDB(simulator.getCDB());
                simulator.getDivider().acceptIssue(issuee);
            }
            else if(exType == EXEC_TYPE.LOAD && (simulator.getLoader().buff[0] == null || simulator.getLoader().buff[1] == null || simulator.getLoader().buff[2] != null)){
                simulator.getROB().updateInstForIssue(issuee);
                //simulator.getROB().readCDB(simulator.getCDB());
                simulator.getLoader().acceptIssue(issuee);
            }
            else if(exType == EXEC_TYPE.MULT && (simulator.getMult().stations[0] == null || simulator.getMult().stations[1] == null)){
                simulator.getROB().updateInstForIssue(issuee);
                //simulator.getROB().readCDB(simulator.getCDB());
                simulator.getMult().acceptIssue(issuee);
            }
            else if(issuee.opcode == INST_TYPE.HALT){
                simulator.getROB().updateInstForIssue(issuee);
                wait = true;
            }
            else if(issuee.opcode == INST_TYPE.STORE){
                simulator.getROB().updateInstForIssue(issuee);
                //simulator.getROB().readCDB(simulator.getCDB());
            }
            else if(exType == EXEC_TYPE.NONE){
                // do nothing
            }
            else{
                wait = true;
            }
            
            // update program counter
            if(!wait){
                if(!issuee.determineIfBranch()){
                    simulator.setPC(simulator.getPC() + 4);
                }
                else if(issuee.branchPrediction){
                    simulator.setPC(issuee.branchTgt);
                }
            }
            else{
                stallCounter++;
            }
        }
    }

  }
